use blk1010_lib::block::BLOCKS;
use crate::model::{Blk1010Model, Blk1010State, BLK1010H, BLK1010W};
// use log::info;
use tge::{
    context::Context,
    event::{event_check, event_register, timer_stage},
    game::{Model, Render},
    render::sprite::{BorderType, Borders, Sprite, Sprites},
    render::style::{Color, Style},
    render::terminal::Term,
};

const COLORS: [Color; 12] = [
    Color::Red,
    Color::Green,
    Color::Yellow,
    Color::Blue,
    Color::Magenta,
    Color::Cyan,
    Color::LightRed,
    Color::LightGreen,
    Color::LightBlue,
    Color::LightYellow,
    Color::LightMagenta,
    Color::LightCyan,
];

enum BlockRenderType {
    CLEAR,
    FLOW,
    FLOWNEAR,
    NORMAL,
    FULL,
}

pub struct Blk1010Render {
    pub term: Term,
    pub main_scene: Sprites,
}

impl Blk1010Render {
    pub fn new() -> Self {
        let t = Term::new();
        let mut s = Sprites::new("main");
        let mut l = Sprite::new(0, 0, (BLK1010W + 2) as u16, (BLK1010H + 2) as u16);
        l.set_border(
            Borders::ALL,
            BorderType::Rounded,
            Style::default().fg(Color::DarkGray),
        );
        l.content.set_str(
            8,
            0,
            "Block1010 [tge.rs]",
            Style::default().fg(Color::Indexed(252)),
        );

        s.add_by_tag(l, "BLK1010-BORDER");
        s.add_by_tag(
            Sprite::new(1, 1, BLK1010W as u16, BLK1010H as u16),
            "BLK1010",
        );
        s.add_by_tag(
            Sprite::new(2, (BLK1010H + 3) as u16, 10, 5),
            "BLK1010-MINI1",
        );
        s.add_by_tag(
            Sprite::new(13, (BLK1010H + 3) as u16, 10, 5),
            "BLK1010-MINI2",
        );
        s.add_by_tag(
            Sprite::new(24, (BLK1010H + 3) as u16, 10, 5),
            "BLK1010-MINI3",
        );

        s.add_by_tag(Sprite::new(0, 0, 20, 10), "BLK1010-FLOW-NEAR");
        s.add_by_tag(Sprite::new(0, 0, 20, 10), "BLK1010-FLOW");
        s.add_by_tag(Sprite::new(0, 18, 34, 1), "BLK1010-GAMEOVER");
        s.add_by_tag(Sprite::new(0, 19, 34, 1), "BLK1010-MSG");

        event_register("Blk1010.RedrawMini", "draw_mini");
        event_register("Blk1010.RedrawFlow", "draw_flow");

        Self {
            term: t,
            main_scene: s,
        }
    }

    fn set_pos(&mut self, sname: &str, x: u16, y: u16) {
        let l = self.main_scene.get_by_tag(sname);
        l.set_pos(x, y);
    }

    fn set_block(&mut self, sname: &str, x: u16, y: u16, ctype: BlockRenderType, gv: u8) {
        let c1: &str;
        let c2: &str;
        let c3: &str;
        let c4: &str;
        let fg: Color;
        let bg: Color;
        let l = self.main_scene.get_by_tag(sname);
        match ctype {
            BlockRenderType::FLOW => {
                c1 = "░░";
                c2 = "░░";
                c3 = "░░";
                c4 = "░░";
                fg = COLORS[gv as usize % COLORS.len()];
                bg = Color::Reset;
            }
            BlockRenderType::FLOWNEAR => {
                c1 = "['";
                c2 = "']";
                c3 = "['";
                c4 = "']";
                fg = COLORS[gv as usize % COLORS.len()];
                bg = Color::Reset;
            }
            BlockRenderType::NORMAL => {
                c1 = "[º";
                c2 = "º]";
                c3 = "[_";
                c4 = "_]";
                fg = COLORS[gv as usize % COLORS.len()];
                bg = Color::Reset;
            }
            BlockRenderType::FULL => {
                c1 = "--";
                c2 = "==";
                c3 = "--";
                c4 = "==";
                fg = Color::Indexed(24);
                bg = Color::Reset;
            }
            BlockRenderType::CLEAR => {
                c1 = "  ";
                c2 = "  ";
                c3 = "  ";
                c4 = "  ";
                fg = Color::Reset;
                bg = Color::Reset;
            }
        }
        l.content
            .set_str(x * 4, y * 2, c1, Style::default().fg(fg).bg(bg));
        l.content
            .set_str(x * 4 + 2, y * 2, c2, Style::default().fg(fg).bg(bg));
        l.content
            .set_str(x * 4, y * 2 + 1, c3, Style::default().fg(fg).bg(bg));
        l.content
            .set_str(x * 4 + 2, y * 2 + 1, c4, Style::default().fg(fg).bg(bg));
    }

    pub fn draw_flow<G: Model>(&mut self, _ctx: &mut Context, model: &mut G) {
        let d = model.as_any().downcast_ref::<Blk1010Model>().unwrap();
        let ss = ["BLK1010-FLOW", "BLK1010-FLOW-NEAR"];
        for ci in 0..2 {
            let dx: u16;
            let dy: u16;
            for i in 0..5 {
                for j in 0..5 {
                    self.set_block(ss[ci], i, j, BlockRenderType::CLEAR, 0);
                }
            }
            if ci == 0 {
                dx = d.flow_pos.x;
                dy = d.flow_pos.y;
                if d.mini_flow == 255 {
                    continue;
                }
            } else {
                if d.flow_pos_near.x == 255 {
                    return;
                }
                dx = d.flow_pos_near.x * 4 + 1;
                dy = d.flow_pos_near.y * 2 + 1;
            }
            self.set_pos(ss[ci], dx, dy);
            let df = d.mini_flow;
            let h = BLOCKS[df as usize - 1].rect.height as usize;
            let w = BLOCKS[df as usize - 1].rect.width as usize;
            let sx = BLOCKS[df as usize - 1].rect.x as usize;
            let sy = BLOCKS[df as usize - 1].rect.y as usize;
            for i in 0..h {
                for j in 0..w {
                    let gv = BLOCKS[df as usize - 1].grid[i + sy][j + sx] * (df % 12 + 1);
                    match gv {
                        0 => {
                            self.set_block(ss[ci], j as u16, i as u16, BlockRenderType::CLEAR, 0);
                        }
                        _ => {
                            let mut brt = BlockRenderType::FLOW;
                            if ci == 1 {
                                brt = BlockRenderType::FLOWNEAR;
                            }
                            self.set_block(ss[ci], j as u16, i as u16, brt, gv);
                        }
                    }
                }
            }
        }
    }

    pub fn draw_mini<G: Model>(&mut self, _ctx: &mut Context, model: &mut G) {
        let d = model.as_any().downcast_ref::<Blk1010Model>().unwrap();
        let ss = ["BLK1010-MINI1", "BLK1010-MINI2", "BLK1010-MINI3"];
        let sd = Style::default().fg(Color::DarkGray).bg(Color::Reset);
        for c in 0..3 {
            let l = self.main_scene.get_by_tag(ss[c]);
            let dc = d.mini_blks[c];
            for i in 0..5 {
                for j in 0..5 {
                    if dc == 255 {
                        l.content.set_str(j * 2, i, ". ", sd);
                    } else {
                        let gv =
                            BLOCKS[dc as usize - 1].grid[i as usize][j as usize] * (dc % 14 + 1);
                        match gv {
                            0 => {
                                l.content.set_str(j * 2, i, ". ", sd);
                            }
                            _ => {
                                let cl = COLORS[gv as usize % COLORS.len()];
                                l.content.set_str(j * 2, i, "[]", Style::default().fg(cl));
                            }
                        }
                    }
                }
            }
        }
    }

    pub fn draw_grid<G: Model>(&mut self, ctx: &mut Context, model: &mut G) {
        let d = model.as_any().downcast_ref::<Blk1010Model>().unwrap();
        let l = self.main_scene.get_by_tag("BLK1010-GAMEOVER");
        let msg: String;
        let msg2: String;
        if ctx.state == Blk1010State::Gameover as u8 {
            l.content.set_str(
                0,
                0,
                "  Game over, press <R> restart...",
                Style::default().fg(COLORS[9]),
            );
        } else {
            if d.map.combo >= 3 {
                msg = format!(
                    "    score : {:?}  combo : {:?}      ",
                    d.map.score, d.map.combo
                );
            } else {
                msg = format!("    score : {:?}                    ", d.map.score);
            }
            l.content.set_str(
                0,
                0,
                msg,
                Style::default().bg(Color::Reset).fg(Color::Reset),
            );
        }
        let lm = self.main_scene.get_by_tag("BLK1010-MSG");
        msg2 = format!("    stage={},fill={}", d.fillman.stage, d.fillman.msg_debug);
        lm.content.set_str(
            0,
            0,
            msg2,
            Style::default().bg(Color::Reset).fg(Color::Reset),
        );
        let frs = timer_stage("Blk1010.Clear");
        for i in 0..BLK1010H / 2 {
            for j in 0..BLK1010W / 4 {
                let gv = d.map.grid[i * d.map.col as usize + j];
                match gv {
                    0 => {
                        self.set_block("BLK1010", j as u16, i as u16, BlockRenderType::CLEAR, 0);
                    }
                    _ => {
                        let mut ctype = BlockRenderType::NORMAL;
                        if frs != 0
                            && frs / 3 % 2 == 0
                            && (d.map.full_cols.contains(&(j as u8))
                                || d.map.full_rows.contains(&(i as u8)))
                        {
                            ctype = BlockRenderType::FULL;
                        }
                        self.set_block("BLK1010", j as u16, i as u16, ctype, gv);
                    }
                }
            }
        }
    }
}

impl Render for Blk1010Render {
    fn init(&mut self, ctx: &mut Context) {
        ctx.adapter.init(
            BLK1010W as u16 + 20,
            BLK1010H as u16 + 10,
            2.0,
            1.0,
            "blk1010".to_string(),
        );
        self.term.init(ctx);
    }

    fn handle_event<G: Model>(&mut self, ctx: &mut Context, data: &mut G, _dt: f32) {
        if event_check("Blk1010.RedrawMini", "draw_mini") {
            self.draw_mini(ctx, data);
        }
        if event_check("Blk1010.RedrawFlow", "draw_flow") {
            self.draw_flow(ctx, data);
        }
    }

    fn handle_timer<G: Model>(&mut self, _ctx: &mut Context, _model: &mut G, _dt: f32) {
        if event_check("Blk1010.Clear", "clear_timer") {}
    }

    fn draw<G: Model>(&mut self, ctx: &mut Context, data: &mut G, _dt: f32) {
        self.draw_grid(ctx, data);
        self.term
            .draw(ctx, |f| {
                self.main_scene.render_all(f);
            })
            .unwrap();
    }
}
